package MathCore.Renders;

import MathCore.Expression;
import MathCore.Lexeme;
import MathCore.Render;
import MathCore.Symbol;
import java.util.Stack;
import javax.microedition.lcdui.Graphics;

/**
 *
 * @author Александр
 */
public class Stripe extends Block
{
	public Block first=null, last=null;
	public Stripe up=null, down=null;

	private int lenght=0;
	private boolean changed = true;

	private String name = "";

	public Stripe(){}
	public Stripe(String name){ this.name = name; }

	public void draw(Graphics g, int x, int y, int style)
	{
		saveCoordinates(x, y, style);

		// смещаем координаты, учитывая стили
		if(style(style, Block.TOP))		y += (height() - center());
		if(style(style, Block.BOTTOM))	y -= center();
		if(style(style, Block.RIGHT))	x -= width();
		if(style(style, Block.HCENTER))	x -= width()/2;

		// отрисовываем по полученным координатам
		int sx = 0;
		for(Block b=first; b != null; b=b.next)
		{
			b.draw(g, x+sx, y, Block.LEFT|Block.VCENTER);
			sx += b.width() + Render.spacing;
		}

		y -= height() - center();

		if(g != null)
		{
			if(empty())
			{
				g.setStrokeStyle(Graphics.DOTTED);
				g.setColor(0xff0000);
				g.drawRect(x, y, width(), height());
				g.setStrokeStyle(Graphics.SOLID);

				x += 3;
			}
			if(Render.activeBlock == null && Render.activeStripe == this && Render.useCursor)
			{
				g.setColor(Render.linkColor);
				if(!empty()) g.drawLine(x-2, y+2, x-2, y+height()-3);
				else  g.drawLine(x, y+2, x, y+height()-3);
			}
		}
	}

	public Block first()
	{
		return first;
	}

	public Block last()
	{
		return last;
	}

	private void upd()
	{
		center = 0;
		width = 0;
		height = 0;

		// первый проход - определяем центральную линию
		for(Block b=first; b != null; b=b.next) if(!(b instanceof Brace) && b.center() > center) center = b.center();

		// второй проход - определяем высоту и ширину
		for(Block b=first; b != null; b=b.next)
		{
			width += b.width() + Render.spacing;
			if(b instanceof Brace) continue;
			if(b.height() - b.center() > height) height = b.height() - b.center();
		}
		height += center;
		if(height < Render.getFont().getHeight())
		{
			height = Render.getFont().getHeight();
			center = height/2;
		}
		if(width < 6) width = 6;

		// третий проход - определяем высоту скобок
		Stack stack = new Stack();
		for(Block b = first; b != null; b = b.next)
		{
			if(b instanceof Brace)
			{
				Brace brace = (Brace) b;
				switch(brace.getType())
				{
					case Symbol.LEFT_BRACE:
					case Symbol.LEFT_LIST:
						brace.sister = null;
						stack.push(brace);
						break;

					case Symbol.RIGHT_LIST:
					case Symbol.RIGHT_BRACE: 
					{
						if(stack.empty())
						{
							brace.sister = null;
							continue;
						}
						Brace sister = (Brace) stack.peek();
						if(sister.getType() == brace.getType()-1)
						{
							stack.pop();
							sister.sister = brace;
							brace.sister = sister;
							brace.center = sister.center = calcMaxCenter(sister, brace);
							brace.height = sister.height = calcMaxHeight(sister, brace)+brace.center;
						}
						else brace.sister = null;
						break;
					}
				}
			}
		}
		changed = false;	
	}

	private int calcMaxHeight(Block start, Block end)
	{
		int h = Render.getFont().getHeight() /2;
		for(Block b = start; b != end.next; b = b.next)
		{
			if(b instanceof Brace) continue;
			if(b.height() - b.center() > h) h = b.height() - b.center();
		}
		return h;
	}

	private int calcMaxCenter(Block start, Block end)
	{
		int c = Render.getFont().getHeight() /2;
		for(Block b = start; b != end.next; b = b.next)
		{
			if(b instanceof Brace) continue;
			if(b.center() > c) c = b.center();
		}
		return c;
	}

	public int center()
	{
		if(changed) upd();
		return center;
	}

	public int width()
	{
		if(changed) upd();
		return width;
	}

	public int height()
	{
		if(changed) upd();
		return height;
	}

	public Block add(Block l)
    {
	    if(l == null) return null;
		l.next = l.prev = null;
		l.parent = this;

		if(lenght == 0) 
		{
			first = last = l;
		}
	    else if(lenght == 1)
	    {
			last = l;
			first.next = last;
			last.prev = first;
	    }
	    else
	    {
			l.prev = last;
			last.next = l;
			last = l;
	    }

	    lenght++;
		setChange();

	    return l;
    }

	public Block addToTop(Block b)
	{
		if(empty()) return add(b);
		b.parent = this;
		b.next = first;
		first.prev = b;
		first = b;
		lenght++;
		setChange();
		return b;
	}

    public Block remove(Block l)
    {
	    if(l == null) return null;

		if(lenght == 1 && isMember(l))
		{
			last = first = null;
			lenght--;
			setChange();
			return l;
		}

	    if(l.next != null && l.prev != null) // somewhere in middle
	    {
			l.prev.next = l.next;
			l.next.prev = l.prev;
	    }
	    else if(l.next != null && l.prev == null) // first in list
	    {
			first = l.next;
			first.prev = null;
	    }
	    else // end of expression
	    {
			last = l.prev;
			if(last != null) last.next = null;
	    }

	    lenght--;
		setChange();

	    return l;
	}

	public void clear()
	{
	    if(first == null) return;

	    Block l = first, l2;
	    while(l.next != null)
	    {
			l2 = l.next;
			l.next = null;
			l = l2;
	    }

	    first = null;
	    last = null;
	    lenght = 0;
		setChange();
	}

	public boolean empty()
	{
		return lenght == 0 ? true : false;
	}

	public Block insertAfter(Block newlex, Block pivot)
	{
		if(newlex == null) return null;
		newlex.parent = this;

		if(pivot == null)
		{
			newlex.next = first;
			first.prev = newlex;
			first = newlex;

			lenght++;
			setChange();
			return newlex;
		}

		if(!isMember(pivot)) return null;
		else if(pivot == last || lenght==1) return add(newlex);
		else
		{
			Block left = pivot, right = pivot.next;
			if(left != null) left.next = newlex;
			if(right != null) right.prev = newlex;
			newlex.next = right;
			newlex.prev = left;

			lenght++;
			setChange();
			return newlex;
		}
	}

	private boolean isMember(Block lex)
	{
		for(Block l = first; l != null; l = l.next) if(lex == l) return true;
		return false;
	}

	public Stripe squeeze(Block start, Block end)
	{
		// создаем полосу
		Stripe res = new Stripe();

		// проверяем аргументы на валидность
		if(start == null || end==null || !isMember(start) || !isMember(end)) return res;

		// запоминаем важные штуки
		Block prestart=start.prev, pastend=end.next;

		// имплантируем концы цепочки в новую полосу
		res.first = start;
		start.prev = null;

		res.last = end;
		end.next = null;

		// имплантируем новую полосу в донора
		if(prestart==null)
		{
			first = res;
			res.prev = null;
		}
		else
		{
			prestart.next = res;
			res.prev = prestart;
		}

		if(pastend == null)
		{
			last = res;
			res.next = null;
		}
		else
		{
			pastend.prev = res;
			res.next = pastend;
		}
		
		res.parent = this;
		for(Block child = res.first; child != null; child = child.next) child.parent = res;
		lenght -= res.recalcLenght()-1;

		return res;
	}

	public void replace(Block start1, Block end1, Block start2, Block end2)
	{
		start2.prev = start1.prev;
		end2.next = end1.next;

		if(start1 == first) first = start2;
		else start1.prev.next = start2;
		
		if(end1 == last) last = end2;
		else end1.next.prev = end2;

		for(Block child = start2; child != end2; child = child.next) child.parent = this;
		end2.parent = this;
		
		recalcLenght();
	}

	public int recalcLenght()
	{
		lenght = 0;
		for(Block b = first; b != null; b=b.next) lenght++;
		return lenght;
	}

	public void dbg_trace()
	{
		String s;
		System.out.println("Trace start");
		System.out.println("Name: "+name);
		for(Block l = first; l != null; l = l.next)
		{
			s="";

			if(l.prev == null) s += "null";
			else { s += l.prev.toString(); }

			s+=" <- " + l.toString() + " -> ";

			if(l.next == null) s += "null";
			else { s += l.next.toString(); }

			System.out.println("\t"+s);
		}

		super.dbg_trace();

		System.out.println("\tlenght: "+lenght);

		System.out.println("Trace complete");
	}

	public String toString()
	{
		if(name.equals("")) return "<stripe>";
		else return "<stripe "+name+">";
	}

	public Block rightLB(Block start)
	{
		if(start==null) return null;

		int bracketCounter = 0;
		Block lb = null;

		for(Block b = start; b != null; b=b.next)
		{
			switch(b.type())
			{
				case Block.TYPE_OPERAND:
					if(bracketCounter == 0) lb = b;
					break;

				case Block.TYPE_DIVISION:
				case Block.TYPE_POWER:
				case Block.TYPE_NON_STOPING_OPERATOR:
				case Block.TYPE_STOPING_OPERATOR:
					if(bracketCounter == 0) b = null;
					break;

				case Block.TYPE_OPEN_BRACKET:
					bracketCounter++;
					break;

				case Block.TYPE_CLOSE_BRACKET:
					bracketCounter--;
					if(lb == null && bracketCounter==0) lb = b;
					break;
			}
			
			if(b == null) break;
		}

		return lb;
	}

	public Block leftLB(Block start)
	{
		if(start==null) return null;

		int bracketCounter = 0;
		Block lb = null;

		for(Block b = start; b != null; b=b.prev)
		{
			switch(b.type())
			{
				case Block.TYPE_OPERAND:
					if(bracketCounter == 0) lb = b;
					break;

				case Block.TYPE_DIVISION:
				case Block.TYPE_POWER:
				case Block.TYPE_NON_STOPING_OPERATOR:
				case Block.TYPE_STOPING_OPERATOR:
					if(bracketCounter == 0) b = null;
					break;

				case Block.TYPE_OPEN_BRACKET:
					bracketCounter++;
					if(lb == null && bracketCounter==0) lb = b;
					break;

				case Block.TYPE_CLOSE_BRACKET:
					bracketCounter--;
					break;
			}
			if(b == null) break;
		}

		return lb;
	}

	public void parse()
	{
		if(empty()) return;

		for(Block b = first; b != null; b = b.next)
		{
			if(b.type() == Block.TYPE_DIVISION)
			{
				Block llb = leftLB(b.prev);
				Block rlb = rightLB(b.next);

				Stripe up, down;
				if(b.prev != null && llb!=null) up = squeeze(llb, b.prev);
				else up = new Stripe();

				if(b.next != null && rlb!=null) down = squeeze(b.next, rlb);
				else down = new Stripe();

				Fraction f = new Fraction(up, down);
				up.parse();
				down.parse();

				if(llb == null) llb = b;
				else llb = up;
				if(rlb == null) rlb = b;
				else rlb = down;

				replace(llb, rlb, f, f);
				f.link = b.link;
				b = f;
			}
			/*else if(b.type() == Block.TYPE_POWER)
			{
				Block llb = leftLB(b.prev);
				Block rlb = rightLB(b.next);

				Stripe up, down;
				if(b.prev != null && llb!=null) up = squeeze(llb, b.prev);
				else up = new Stripe();

				if(b.next != null && rlb!=null) down = squeeze(b.next, rlb);
				else down = new Stripe();

				Power f = new Power(up, down);
				up.parse();
				down.parse();

				if(llb == null) llb = b;
				else llb = up;
				if(rlb == null) rlb = b;
				else rlb = down;

				replace(llb, rlb, f, f);
				f.link = b.link;
				b = f;
			}*/
		}
	}

	public Block findLinked(Lexeme lex) 
	{
		Block linked;
		for(Block child = first; child != null; child = child.next)
		{
			linked = child.findLinked(lex);
			if(linked != null) return linked;
		}
		return null;
	}

	public void setChange()
	{
		changed = true;
		Block p = parent;
		while(p != null)
		{
			if(p instanceof Stripe) ((Stripe)p).changed = true;
			p = p.parent;
		}
	}

	public void extract(Expression exp)
	{
		for(Block b = first(); b != null; b = b.next)
		{
			b.extract(exp);
		}
	}
}
